package cn.virens.mybatis.mapper.example.util;

import java.io.Serializable;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.virens.common.exception.APIException;

@SuppressWarnings("serial")
public class SQLBuidler implements Serializable {
	private final StringBuilder builder = new StringBuilder();

	public SQLBuidler(String... strArray) {
		this.append(strArray);
	}

	public SQLBuidler append(String... strArray) {
		for (String string : strArray) {
			this.builder.append(string);
		}

		return this;
	}

	public SQLBuidler appendLE(String... strArray) {
		this.builder.append("\r\n");
		this.append(strArray);

		return this;
	}

	public SQLBuidler append(SQLBuidler buidler) {
		return append(buidler.toString());
	}

	/**
	 * 生成 trim 标签包装的 xml 结构
	 *
	 * @param  prefix          前缀
	 * @param  prefixOverrides 前缀替换内容
	 * @param  content         标签中的内容
	 * @return                 trim 标签包装的 xml 结构
	 */
	public SQLBuidler trim(String prefix, String prefixOverrides, SQLSupplier content) throws APIException {
		return append("\n<trim prefix=\"", prefix, "\" prefixOverrides=\"", prefixOverrides, "\">", content.str(), "\n</trim>");
	}

	/**
	 * 生成 trim 标签包装的 xml 结构
	 *
	 * @param  start           SQL 前半段
	 * @param  prefix          前缀
	 * @param  prefixOverrides 前缀替换内容
	 * @param  content         标签中的内容
	 * @return                 trim 标签包装的 xml 结构
	 */
	public SQLBuidler trim(String start, String prefix, String prefixOverrides, SQLSupplier content) throws APIException {
		return append("\n ", start, " <trim prefix=\"", prefix, "\" prefixOverrides=\"", prefixOverrides, "\">", content.str(), "\n</trim>");
	}

	/**
	 * 生成 trim 标签包装的 xml 结构
	 *
	 * @param  prefix          前缀
	 * @param  suffix          后缀
	 * @param  prefixOverrides 前缀替换内容
	 * @param  suffixOverrides 后缀替换内容
	 * @param  content         标签中的内容
	 * @return                 trim 标签包装的 xml 结构
	 */
	public SQLBuidler trim(String prefix, String suffix, String prefixOverrides, String suffixOverrides, SQLSupplier content) throws APIException {
		return append("\n<trim prefix=\"", prefix, "\" prefixOverrides=\"", prefixOverrides, "\" suffixOverrides=\"", suffixOverrides, "\" suffix=\"", suffix, "\">", content.str(), "\n</trim>");
	}

	/**
	 * 生成 trim 标签包装的 xml 结构
	 *
	 * @param  start           SQL 前半段
	 * @param  prefix          前缀
	 * @param  suffix          后缀
	 * @param  prefixOverrides 前缀替换内容
	 * @param  suffixOverrides 后缀替换内容
	 * @param  content         标签中的内容
	 * @return                 trim 标签包装的 xml 结构
	 */
	public SQLBuidler trim(String start, String prefix, String suffix, String prefixOverrides, String suffixOverrides, SQLSupplier content) throws APIException {
		return append("\n ", start, " <trim prefix=\"", prefix, "\" prefixOverrides=\"", prefixOverrides, "\" suffixOverrides=\"", suffixOverrides, "\" suffix=\"", suffix, "\">", content.str(), "\n</trim>");
	}

	/**
	 * 生成 foreach 标签包装的 xml 结构
	 *
	 * @param  collection 遍历的对象
	 * @param  item       对象名
	 * @param  content    标签中的内容
	 * @return            foreach 标签包装的 xml 结构
	 */
	public SQLBuidler foreach(String collection, String item, SQLSupplier content) throws APIException {
		return append("\n<foreach collection=\"", collection, "\" item=\"", item, "\">", content.str(), "</foreach>");
	}

	/**
	 * 生成 foreach 标签包装的 xml 结构
	 *
	 * @param  collection 遍历的对象
	 * @param  item       对象名
	 * @param  separator  连接符
	 * @param  content    标签中的内容
	 * @return            foreach 标签包装的 xml 结构
	 */
	public SQLBuidler foreach(String collection, String item, String separator, SQLSupplier content) throws APIException {
		return append("\n<foreach collection=\"", collection, "\" item=\"", item, "\" separator=\"", separator + "\">", content.str(), "</foreach>");
	}

	/**
	 * 生成 foreach 标签包装的 xml 结构
	 *
	 * @param  collection 遍历的对象
	 * @param  item       对象名
	 * @param  separator  连接符
	 * @param  open       开始符号
	 * @param  close      结束符号
	 * @param  content    标签中的内容
	 * @return            foreach 标签包装的 xml 结构
	 */
	public SQLBuidler foreach(String collection, String item, String separator, String open, String close, SQLSupplier content) throws APIException {
		return append("\n<foreach collection=\"", collection, "\" item=\"", item, "\" open=\"", open, "\" close=\"", close, "\" separator=\"", separator, "\">", content.str(), "</foreach>");
	}

	/**
	 * 生成 foreach 标签包装的 xml 结构
	 *
	 * @param  collection 遍历的对象
	 * @param  item       对象名
	 * @param  separator  连接符
	 * @param  open       开始符号
	 * @param  close      结束符号
	 * @param  index      索引名（list为索引，map为key）
	 * @param  content    标签中的内容
	 * @return            foreach 标签包装的 xml 结构
	 */
	public SQLBuidler foreach(String collection, String item, String separator, String open, String close, String index, SQLSupplier content) throws APIException {
		return append("\n<foreach collection=\"", collection, "\" item=\"", item, "\" index=\"", index, "\" open=\"", open, "\" close=\"", close, "\" separator=\"", separator, "\">", content.str(), "</foreach>");
	}

	/**
	 * 生成 choose 标签包装的 xml 结构
	 *
	 * @param  content 标签中的内容
	 * @return         choose 标签包装的 xml 结构
	 */
	public SQLBuidler choose(SQLSupplier content) throws APIException {
		return append("\n<choose>", content.str(), "\n</choose>");
	}

	/**
	 * 生成 otherwise 标签包装的 xml 结构
	 *
	 * @param  content 标签中的内容
	 * @return         otherwise 标签包装的 xml 结构
	 */
	public SQLBuidler chooseOther(SQLSupplier content) throws APIException {
		return append("\n<otherwise>", content.str(), "\n</otherwise>");
	}

	/**
	 * 生成 when 标签包装的 xml 结构
	 *
	 * @param  test    when 的判断条件
	 * @param  content 标签中的内容
	 * @return         when 标签包装的 xml 结构
	 */
	public SQLBuidler chooseWhen(String test, SQLSupplier content) throws APIException {
		return append("\n<when test=\"", test, "\">", content.str(), "\n</when>");
	}

	/**
	 * 生成 set 标签包装的 xml 结构
	 *
	 * @param  content 标签中的内容
	 * @return         set 标签包装的 xml 结构
	 */
	public SQLBuidler set(SQLSupplier content) throws APIException {
		return append("\n<set>", content.str(), "\n</set>");
	}

	/**
	 * 生成 set 标签包装的 xml 结构
	 *
	 * @param  content 标签中的内容
	 * @return         set 标签包装的 xml 结构
	 */
	public SQLBuidler set(String delimiter, Supplier<Stream<String>> content) throws APIException {
		return set(b -> b.append(content.get().collect(Collectors.joining(delimiter))));
	}

	/**
	 * 生成 if 标签包装的 xml 结构
	 *
	 * @param  test    if 的判断条件
	 * @param  content 标签中的内容
	 * @return         if 标签包装的 xml 结构
	 */
	public SQLBuidler ifTest(String test, SQLSupplier content) throws APIException {
		return append("\n<if test=\"", test + "\">", content.str(), "</if>");
	}

	/**
	 * 生成 if 标签包装的 xml 结构(属性为空)
	 *
	 * @param  variable if 的判断变量
	 * @param  content  标签中的内容
	 * @return          if 标签包装的 xml 结构
	 */
	public SQLBuidler ifEmpty(String variable, SQLSupplier content) throws APIException {
		return ifTest(isEmpty(variable), content);
	}

	/**
	 * 生成 if 标签包装的 xml 结构(属性不为空)
	 *
	 * @param  variable if 的判断变量
	 * @param  content  标签中的内容
	 * @return          if 标签包装的 xml 结构
	 */
	public SQLBuidler ifNotEmpty(String variable, SQLSupplier content) throws APIException {
		return ifTest(isNotEmpty(variable), content);
	}

	/**
	 * 生成参数验证代码
	 * 
	 * @param  variable     判断变量
	 * @return
	 * @throws APIException
	 */
	public SQLBuidler assertNotNull(String variable) throws APIException {
		return append("${", notNull(variable), "}\n");
	}

	@Override
	public String toString() {
		return builder.toString();
	}

	@FunctionalInterface
	public interface SQLSupplier extends Consumer<SQLBuidler> {
		default String str() throws APIException {
			return get().toString();
		}

		default SQLBuidler get() throws APIException {
			SQLBuidler answer = of();

			this.accept(answer);

			return answer;
		}
	}

	public static SQLBuidler of(String... strArray) {
		return new SQLBuidler(strArray);
	}

	public static String notNull(String variable) throws APIException {
		return "@cn.virens.mybatis.mapper.example.util.Util@notNull(" + variable + ")";
	}

	public static String isEmpty(String variable) throws APIException {
		return "@cn.virens.mybatis.mapper.example.util.Util@isEmpty(" + variable + ")";
	}

	public static String isNotEmpty(String variable) throws APIException {
		return "@cn.virens.mybatis.mapper.example.util.Util@isNotEmpty(" + variable + ")";
	}
}
